package com.pat.jrgwt.client;

import java.util.Date;
import java.util.HashMap;

import sun.security.jca.GetInstance;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dev.json.JsonObject;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.pat.jrgwt.shared.MyConstants;
import com.google.gwt.user.client.ui.HorizontalPanel;

public class ArchivePerf extends Composite
{

    public final static int PerfUnlevelColumn = 2;
    public final static int YearPerfColumn = 3;
    public final static int SumColumn = 4;
    public final static int MaxPerfColumn = 5;
    public final static int DrawdownColumn = 6;
    public final static int MaxDdColumn = 7;
    public final static int PerfLevColumn = 8;
    public final static int YearPerfLevColumn = 9;
    public final static int SumLevColumn = 10;
    public final static int MaxDdLevColumn = 11;
    
    private static ArchivePerfUiBinder uiBinder = GWT
            .create(ArchivePerfUiBinder.class);
    
    // Contains data w/ percents
    protected HashMap<Integer, double[]> m_arPerfByYear = new HashMap<Integer, double[]>();
    
    /**
     * How many years to be displayed
     */
    protected int m_totalYears = 8;
    
    /**
     * First year to be displayed
     */
    protected int m_startYear = 1900 + new Date().getYear();
    
    @UiField FlexTable ArchivePerfTable;
    @UiField HorizontalPanel ArchivePerfHeader;
    protected boolean m_isLoaded = false;

    interface ArchivePerfUiBinder extends UiBinder<Widget, ArchivePerf>
    {
    }
    
    // Multi-thread safe singleton holder
    private static class SingletonHolder
    {
        private static final ArchivePerf instance = new ArchivePerf();
    }
    
    /**
     * Retruns instance of ArchivePerf, threa safe
     * 
     * @return ArchivePerf
     */
    public static ArchivePerf getInstance()
    {
        return SingletonHolder.instance;
    }

    private ArchivePerf()
    {
        JrcEventsLog.getInstance().add("Archive perf is initializing...");
        initWidget(uiBinder.createAndBindUi(this));
    }
    
    public void setData(HashMap<Integer, double[]> arPerfByYear)
    {
        m_arPerfByYear = arPerfByYear;
        m_isLoaded = true;
    }
    
    
    public void redraw()
    {
//        loadData();
        formatFullGrid(m_totalYears);
        recalculateData();
    }
    
    public double getValueBy(int year, int month, int column)
    {
        int firstYear = m_startYear - MyConstants.TOTAL_YEARS;
//        GWT.log("ArchivePerf.getValueBy(" + year + ", " + month + ", " + column + ")");
        int row = 12 * (year - firstYear) + month - 1;
//        GWT.log("row to get it: " + row);
        return getSafe(row, column);
    }
    
    /**
     * Returns value at specified place or Double.NaN in case of error
     * @param row
     * @param column
     * @return
     */
    public double getSafe(int row, int column)
    {
        double v = Double.NaN;
        
        try
        {
            v = Double.parseDouble(ArchivePerfTable.getText(row, column));
        }
        catch (NumberFormatException nfe)
        {
            GWT.log("Error getting ArchivePerf value at: (row=" + row + ", column=" + column + ")" );
        }
        
        return v;
    }
    
    public void recalculateData()
    {
        final double initialPerf = 100;
        final double initialPerfLevPct = 5;
        final double drawdownDefault = 0.0;
        
        double previousPerf = initialPerf;
        double previousSumLev = initialPerf;
        double previousDrawdown = drawdownDefault;
        
        double perf = 0;
        double sumPerf = 0;
        double maxPerf = 0;
        double maxDrawdown = 0;
        double drawdown = 0;
        int row = 0;
        int year = 0;
        
        double totalYearPerf = 0;
        double totalYearPerfUnlev = 0;

        // To format 2 digits precision
        NumberFormat nf = NumberFormat.getFormat("###,##0.00");
        NumberFormat nf4 = NumberFormat.getFormat("###,##0.0000");
        
        for( int i = 0; i < m_totalYears; i++)
        {
            year = m_startYear + i - m_totalYears;
            totalYearPerf = 0;
            totalYearPerfUnlev = 0;
            for( int month = 1; month < 13; month++)
            {
                // Get perf for current month
                perf = getPerfByDate(year, month);

                if( (perf == Double.MIN_VALUE) || (perf == 0) )
                {
                    row++;
                    continue;
                }

                ArchivePerfTable.setText(row, 2, nf.format(perf));
                totalYearPerfUnlev += perf;

                sumPerf = perf + previousPerf;
                ArchivePerfTable.setText(row, 4, nf.format(sumPerf));
                
                // Find max
                if( sumPerf > maxPerf )
                {
                    maxPerf = sumPerf;
                }
                ArchivePerfTable.setText(row, 5, nf.format(maxPerf) );
                
//                GWT.log("current perf: " + sumPerf + ", previous perf: " + previousPerf + ", max perf: " + maxPerf);
                if( sumPerf < previousPerf )
                {
                    drawdown = maxPerf - sumPerf;
                }else
                {
                    drawdown = previousDrawdown;
                }
                ArchivePerfTable.setText(row, 6, nf4.format(drawdown));
                
                // Find max drawdown
                if( maxDrawdown < drawdown )
                {
                    maxDrawdown = drawdown;
                }
                ArchivePerfTable.setText(row, 7, nf4.format(maxDrawdown));
                
                // Perf (lev. %)
                ArchivePerfTable.setText(row, 8, nf.format(perf * initialPerfLevPct));
                totalYearPerf += perf * initialPerfLevPct;
                
                double currentSum = previousSumLev + perf * initialPerfLevPct;
                ArchivePerfTable.setText(row, 10, nf.format(currentSum));
                
                ArchivePerfTable.setText(row, 11, nf4.format(maxDrawdown * initialPerfLevPct));
                
                previousDrawdown = drawdown;
                previousSumLev = currentSum;
                previousPerf = sumPerf;
                row++;
            }  // END FOR (months in year)
            
            ArchivePerfTable.getCellFormatter().setStyleName(row - 1, 9, "cellBold");
            ArchivePerfTable.setText(row - 1, 9, nf.format(totalYearPerf));

            ArchivePerfTable.getCellFormatter().setStyleName(row - 1, 3, "cellBold");
            ArchivePerfTable.setText(row - 1, 3, nf.format(totalYearPerfUnlev));

        }  // END FOR (years)
    }
    
    /**
     * Returns value by year and month (1-12). No exception if out of range
     */
    public double getPerfByDate(int year, int month)
    {
        double v = Double.MIN_VALUE;
        
        if( m_arPerfByYear.containsKey(year) )
        {
            if( (month > 0) && (month < 13) )
            {
                v = m_arPerfByYear.get(year)[month - 1];
            }else
            {
//                GWT.log("No value for archive perf month " + month + " in year " + year);
            }
        }else
        {
//            GWT.log("No for archive perf year " + year);
        }
        
        return v;
    }
    

    protected void formatFullGrid(int totalYears)
    {
        ArchivePerfTable.setBorderWidth(1);
        ArchivePerfTable.setCellPadding(3);
        ArchivePerfTable.setCellSpacing(2);

        String[] arHeaders = new String[] { "Year", "Month", "Perf. (unlev., %)", "Year perf. (unlev., %)", "Sum (unlev. %)", "Max perf. (unlev. %)", "Drawdown (unlev. %)", "Max DD (unlev. %)", "Perf. (lev., %)", "Year perf. (lev., %)", "Sum (lev. %)", "Max DD (lev. %)" };
        
        // Format header for cell
        for (int i = 0; i < 12; i++)
        {
            Label l = new Label(arHeaders[i]);
            l.setWidth("65px");
            l.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
            ArchivePerfHeader.add(l);
            ArchivePerfTable.getCellFormatter().setWidth(0, i, "60px");
        }
        
        for (int i = totalYears; i >= 0; i--)
        {
            //ArchivePerfTable.setText(now.getYear() + 1900 - i, 0, "" + i);
            formatOneYearGrid(m_startYear - i, (totalYears - i) * 12);
        }
    }
    
    protected void formatOneYearGrid(int year, int startRow)
    {
        // Display year
        ArchivePerfTable.setText(startRow, 0, "" + year);
        ArchivePerfTable.getCellFormatter().setStyleName(startRow, 0, "cellBold");
        
        // Sum for 12 months
        double total = 0.0;
        NumberFormat nf = NumberFormat.getFormat("###,##0.##");
        for (int i = 0; i < 12; i++)
        {
            double[] arPerf = m_arPerfByYear.containsKey(year) ? m_arPerfByYear.get(year) : new double[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
            ArchivePerfTable.setText(startRow + i, 1, "" + (i+1));
            ArchivePerfTable.setText(startRow + i, 2, "" + nf.format(arPerf[i]));
            total += arPerf[i];
            
            // Format empty cells
            for (int j = 3; j < 12; j++)
            {
                ArchivePerfTable.setText(startRow + i, j, "");
            }
        }
        ArchivePerfTable.getCellFormatter().setStyleName(startRow + 11, 3, "cellBold");
        ArchivePerfTable.setText(startRow + 11, 3, nf.format(total));
    }
    
//    protected void loadData()
//    {
//        m_arPerfByYear = new HashMap<Integer, double[]>();
//        
//        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getarchiveperf?callback=", new JsonRequestHandler()
//        {
//            
//            @Override
//            public void onRequestCompleted(JavaScriptObject json)
//            {
//                JrcEventsLog.getInstance().add("Got archive perf from server");
//                parseAndStoreJson(json);
//                recalculateData();
//            }
//        });
//        
////        m_arPerfByYear.put(2003, new double[] { 0, 0, 0, 0, 0, 0, 0, 1.16128490960275, 1.36, 0.71, 0.34, 0.02 });
////        m_arPerfByYear.put(2004, new double[] { 2.63, 1.50, 2.13, 1.78, 2.46, 1.60, 0.31, 1.06, 0.83, 0.82, 1.28, 1.85 });
////        m_arPerfByYear.put(2005, new double[] { 0.53, 0.23, -0.15, 0.54, 0.24, 1.48, 0.72, 1.05, -0.27, 0.94, 0.66, 0.48 });
//    }
    
    /**
     * Parses and returns HashMap from JSON data
     */
    public static HashMap<Integer, double[]> parseJsonData(JavaScriptObject jso)
    {
        HashMap<Integer, double[]> arPerfByYear = new HashMap<Integer, double[]>();
        JSONObject jsonObj = new JSONObject(jso);
        try
        {
            for( String k: jsonObj.keySet() )
            {
                // Get year and all values for this year
                int year = Integer.parseInt(k);
                JSONArray ar = jsonObj.get(k).isArray();
                double[] values = new double[12];
                // Loop JSON array (12 months in year) and convert to double
                for (int i = 0; i < ar.size(); i++)
                {
                    values[i] = Double.parseDouble(ar.get(i).toString().replaceAll("\"", ""));
                }
                
                arPerfByYear.put(year, values);
            }
        }
        catch( Exception e )
        {
            GWT.log("Error parsing ArchivePerf data from ITTC server! " + e.toString());
        }
        
        return arPerfByYear;
    }

    public boolean isDataLoaded()
    {
        return m_isLoaded;
    }
    
}
